/*--------------------------+
|	   Minigame Class		|
|	---------------------	|
|	Runs the Audiosurf-y	|
|	Minigame to unlock		|
|	bonus loot rooms		|
+--------------------------*/

#include "MinigameState.h"

// Wrappers
#include "SGD Wrappers\CSGD_Direct3D.h"
#include "SGD Wrappers\CSGD_TextureManager.h"
#include "SGD Wrappers\CSGD_DirectInput.h"
#include "SGD Wrappers\CSGD_XAudio2.h"
#include "BitmapFont.h"

#include "Emitter.h"
#include <string>

// Access the game class
#include "Game.h"
#include "GamePlayState.h"

// Constructor
MinigameState::MinigameState(void) : TimeRemaining(10.0f), LivesRemaining(3), Failed(false), ObstacleTimer(0.0f), Background(-1), Ships(-1), Trail(NULL), BadTrails(NULL), BackgroundY(0), FailSound(-1), playerNum(0) { }

// Destructor
MinigameState::~MinigameState(void) { }

// Get access to the singleton
MinigameState* MinigameState::GetInstance()
{
	static MinigameState instance;
	return &instance;
}

// Initialization
void MinigameState::Enter(void)
{
	// Load images
	Background = CSGD_TextureManager::GetInstance()->LoadTexture(_T("graphics/minigame_background.png"),D3DCOLOR_XRGB(0,255,0));
	Ships = CSGD_TextureManager::GetInstance()->LoadTexture(_T("graphics/minigame_ships.png"),D3DCOLOR_XRGB(0,255,0));

	// Load Sounds
	FailSound = CSGD_XAudio2::GetInstance()->SFXLoadSound(_T("Sound/minigame_hit.wav"));

	// Create the template emitter for the enemy ship trails
	Emitter* temp = Emitter::LoadEmitter("emitters/minigame_badtrail.xml");

	// Create the obstacles
	while( Obstacles.size() < 8 )
	{
		// Create a new obstacle
		Entity* obstacle = new Entity();
		obstacle->SetY(-64);
		int r = rand()%3;
		// Put it on a random track
		switch ( r )
		{
		case 0:
			obstacle->SetX(Game::GetInstance()->GetScreenWidth()/2.0f + 128);
			break;
		case 1:
			obstacle->SetX(Game::GetInstance()->GetScreenWidth()/2.0f);
			break;
		case 2:
			obstacle->SetX(Game::GetInstance()->GetScreenWidth()/2.0f - 128);
			break;
		}

		obstacle->SetY(9001);

		// Add the obstacle to the list
		Obstacles.push_back(obstacle);

		// Create the obstacles tail
		Emitter* e = new Emitter(*temp);
		BadTrails.push_back(e);
	}

	delete temp;

	// Set up the emitter
	Trail = Emitter::LoadEmitter("emitters/minigame_shiptrail.xml");

	// Set up the player's ship
	PlayerShip.SetX(Game::GetInstance()->GetScreenWidth()/2.0f);
	PlayerShip.SetY(Game::GetInstance()->GetScreenHeight()*0.75f);

	// Set the timer
	TimeRemaining = 15.0f;
	ObstacleTimer = 0.0f;

	// Set the loss indicator
	Failed = false;
	Won = false;

	// Set the difficulty
	CurDifficulty = (Difficulty)(rand()%3);
	LivesRemaining = 3 - CurDifficulty;

	// Set the existing obstacles to be out of the way
	for(uint i = 0; i < Obstacles.size(); ++i)
	{
		Obstacles[i]->SetY(9001);
		BadTrails[i]->SetPosition(Obstacles[i]->GetX(),Obstacles[i]->GetY());
		BadTrails[i]->Loop();
	}

	// Run the emitter
	Trail->Loop();
}

// Player Input
bool MinigameState::Input(void)
{
	// Get the input wrapper
	CSGD_DirectInput* DI = CSGD_DirectInput::GetInstance();

	// Move the PlayerShip
	if( DI->JoystickGetLStickDirDown( DIR_LEFT, playerNum )  /*DI->JoystickGetLStickXNormalized(playerNum) < 0*/ && DI->JoystickGetLStickXAmount(playerNum) < -500)
		PlayerShip.SetX(Game::GetInstance()->GetScreenWidth()/2.0f - 128);
	else if( DI->JoystickGetLStickDirDown( DIR_RIGHT, playerNum ) /*DI->JoystickGetLStickXNormalized(playerNum) > 0*/ && DI->JoystickGetLStickXAmount(playerNum) > 500)
		PlayerShip.SetX(Game::GetInstance()->GetScreenWidth()/2.0f + 128);
	else
		PlayerShip.SetX(Game::GetInstance()->GetScreenWidth()/2.0f);

	return true;
}

// Update the game objects
void MinigameState::Update(float elapsedTime)
{ 
	// Update the time left to win
	TimeRemaining -= elapsedTime;
	if( TimeRemaining <= 0 )
		Won = true;

	// Update the time till the next obstacle is dropped
	ObstacleTimer -= elapsedTime;
	if(  ObstacleTimer <= 0 )
		ObstacleTimer = 0;

	// Move the background
	BackgroundY += elapsedTime * 200;
	if( BackgroundY >= 600 )
		BackgroundY = -600;

	// Check collision
	for( unsigned int i = 0; i < Obstacles.size(); ++i )
	{
		if( abs(Obstacles[i]->GetX() - PlayerShip.GetX()) < 64 &&
			abs(Obstacles[i]->GetY() - PlayerShip.GetY()) < 64 )
		{
			Obstacles[i]->SetY(Game::GetInstance()->GetScreenHeight() + 64.0f);
			TimeRemaining += 3.0f;
			CSGD_XAudio2::GetInstance()->SFXPlaySound(FailSound);
			if( --LivesRemaining <= 0 )
			{
				LivesRemaining = 0;
				Failed = true;
			}
		}
	}

	// Get the cam position
	int camx = GamePlayState::GetInstance()->GetCamX();
	int camy = GamePlayState::GetInstance()->GetCamY();

	// Move the obstacles
	for( unsigned int i = 0; i < Obstacles.size(); ++i )
	{
		Obstacles[i]->SetY(Obstacles[i]->GetY() + 512 * elapsedTime);
		if( Obstacles[i]->GetY() > Game::GetInstance()->GetScreenWidth() && ObstacleTimer == 0 )
		{
			Obstacles[i]->SetY(-64);
			int r = rand()%3;
			switch ( r )
			{
			case 0:
				Obstacles[i]->SetX(Game::GetInstance()->GetScreenWidth()/2.0f + 128);
				break;
			case 1:
				Obstacles[i]->SetX(Game::GetInstance()->GetScreenWidth()/2.0f);
				break;
			case 2:
				Obstacles[i]->SetX(Game::GetInstance()->GetScreenWidth()/2.0f - 128);
				break;
			}
			ObstacleTimer = 1.2f - 0.2f * CurDifficulty;
		}
		BadTrails[i]->SetPosition(Obstacles[i]->GetX()+camx,Obstacles[i]->GetY()+camy);
		BadTrails[i]->Update(elapsedTime);
	}

	// Update the trail emitter
	Trail->SetPosition(PlayerShip.GetX()+camx, PlayerShip.GetY()+camy);
	Trail->Update(elapsedTime);
}

// Draw the game
void MinigameState::Render(void)
{
	// Draw the Background
	CSGD_TextureManager::GetInstance()->Draw(Background,86,(int)BackgroundY);
	CSGD_TextureManager::GetInstance()->Draw(Background,86,(int)BackgroundY + 600);
	CSGD_TextureManager::GetInstance()->Draw(Background,86,(int)BackgroundY - 600);

	// Draw the trails
	Trail->Render();
	CSGD_Direct3D::GetInstance()->GetSprite()->Flush();

	// Draw the Ship
	RECT r = {0,0,36,36};
	CSGD_TextureManager::GetInstance()->Draw(Ships,(int)PlayerShip.GetX() - 36,(int)PlayerShip.GetY() - 36,2.0f,2.0f,&r,0,0,0,D3DCOLOR_XRGB(0,150,255));

	r.left	 = 37;
	r.top	 = 0;
	r.right  = 37 + 36;
	r.bottom = 36;

	// Draw the Obstacles
	for( uint i = 0; i < Obstacles.size(); ++i )
	{
		BadTrails[i]->Render();
		CSGD_TextureManager::GetInstance()->Draw(Ships,(int)Obstacles[i]->GetX() - 36,(int)Obstacles[i]->GetY() + 36,2.0f,-2.0f,&r,0,0,0,D3DCOLOR_XRGB(255,50,50));
	}

	// Draw the remaining lives
	RECT l = {0,0,36,36};
	for( uint i = 0; i < LivesRemaining; ++i )
		CSGD_TextureManager::GetInstance()->Draw(Ships,550 + 38*i,550,1,1,&l,0,0,0,D3DCOLOR_XRGB(0,150,255));

	// Draw the remaining time
	char s[30] = "";
	sprintf_s(s,"TIME REMAINING: %f",TimeRemaining);
	s[20] = '\0';
	Game::GetInstance()->DrawBitmapFont(s,110,550,1,D3DCOLOR_XRGB(255,255,255));
}

// Shutdown
void MinigameState::Exit(void)
{
	// Unload Images
	if( Background != -1 )
	{
		CSGD_TextureManager::GetInstance()->UnloadTexture(Background);
		Background = -1;
	}
	if( Ships != -1 )
	{
		CSGD_TextureManager::GetInstance()->UnloadTexture(Ships);
		Ships = -1;
	}

	// Unload sound
	if( FailSound != -1 )
	{
		if( CSGD_XAudio2::GetInstance()->SFXIsSoundPlaying(FailSound) )
			CSGD_XAudio2::GetInstance()->SFXStopSound(FailSound);
		CSGD_XAudio2::GetInstance()->SFXUnloadSound(FailSound);
		FailSound = -1;
	}

	// Clean up Particles
	if( Trail != NULL )
	{
		Trail->Shutdown();
		delete Trail;
		Trail = NULL;
	}
	while( BadTrails.size() )
	{
		int i = BadTrails.size() - 1;
		BadTrails[i]->Shutdown();
		delete BadTrails[i];
		BadTrails[i] = NULL;
		BadTrails.pop_back();
	}

	// Clean up obstacles
	while( Obstacles.size() )
	{
		int i = Obstacles.size() -1;
		Obstacles[i]->Release();
		Obstacles[i] = NULL;
		Obstacles.pop_back();
	}
}

// Run the minigame
void MinigameState::StartGame(int num)
{
	// Which player is playing the game
	playerNum = num;

	// Set up the player's ship
	PlayerShip.SetX(Game::GetInstance()->GetScreenWidth()/2.0f);
	PlayerShip.SetY(Game::GetInstance()->GetScreenHeight()*0.75f);

	// Set the timer
	TimeRemaining = 15.0f;
	ObstacleTimer = 0.0f;

	// Set the loss indicator
	Failed = false;
	Won = false;

	// Set the difficulty
	CurDifficulty = (Difficulty)(rand()%3);
	LivesRemaining = 3 - CurDifficulty;

	// Set the existing obstacles to be out of the way
	for(uint i = 0; i < Obstacles.size(); ++i)
	{
		Obstacles[i]->SetY(9001);
		BadTrails[i]->SetPosition(Obstacles[i]->GetX(),Obstacles[i]->GetY());
	}
}